[ゆっくり] AquesTalk2をRustから使う
Introduction
このあいだ、たまたま自分が昔(10年前)書いた記事をみたらAquesTalk2をつかってなんかやってました。
このときはEC2でCのプログラムコードを使っていたのですが、
今回はローカル(OrbStack)環境で、AquestTalk2のライブラリを
Rustからつかってみます。
※今回もAquestTalk2の評価版を使っています。
評価版では「ナ行、マ行」が「ぬ」になります。
製品版を使用するにはライセンスが必要なので、詳しくはこちらをご確認ください。
Environments
- MacBook Pro (13-inch, M1, 2020)
- OS : MacOS 14.3.1
- OrbStack : 1.4.3
Setup
まずはOrbStackでLinux仮想環境を作成します。
Ubuntuを選択し、CPU TypeはIntel(Appleだと動かなかった)を選択して
起動しましょう。
SSHでログインして必要なパッケージをインストールします。
% sudo apt update % sudo apt install unzip % sudo apt install -y build-essential % sudo apt install libclang-dev
AquesTalk2をダウンロードして、lib64ディレクトリにあるlibAquesTalk2Evaの
リンクを作成しておきます。
% curl -o aqtk2-lnx-eva_230.zip https://www.a-quest.com/archive/package/aqtk2-lnx-eva_230.zip % unzip aqtk2-lnx-eva_230.zip % cd aqtk2-lnx-eva_230/lib64 % ln -s libAquesTalk2Eva.so.2.3 libAquesTalk2.so.2 % ln -s libAquesTalk2Eva.so.2.3 libAquesTalk2.so $ ls -l /path/your/aqtk2-lnx-eva/lib64/ AquesTalk2.h libAquesTalk2.so -> libAquesTalk2Eva.so.2.3 libAquesTalk2.so.2 -> libAquesTalk2Eva.so.2.3 libAquesTalk2Eva.so.2.3
Rust環境をインストールしてCargoでプロジェクトを作成しましょう。
% curl https://sh.rustup.rs -sSf | sh ・・・ % cargo new yukkuri
Try Yukkuri
プロジェクトを作成したので、
Aquestalk2ライブラリをRustから使ってみます。
まずはCargo.tomlに依存ライブラリを記述します。
[dependencies] libc = "0.2.153" [build-dependencies] bindgen = "0.59.1" cc = "1.0"
次に、プロジェクトのルートにbuild.rsを作成します。
build.rsではビルド実行時にbindgenでAquesTalk2の
Rustバインディングを生成します。
このファイルはbindings.rsという名前で指定した場所へ出力されます。
extern crate bindgen; use std::env; use std::path::PathBuf; fn main() { // Aquestal2のライブラリパス let lib_path = "/path/your/aqtk2-lnx-eva/lib64"; // AquesTalk2のheader file let header_path = format!("{}/AquesTalk2.h", lib_path); // generate bindings.rs let bindings = bindgen::Builder::default() .header(&header_path) .generate() .expect("Unable to generate bindings"); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); bindings .write_to_file(out_path.join("bindings.rs")) .expect("Couldn't write bindings!"); // AquesTalk2のライブラリのパスを指定 println!("cargo:rustc-link-search=native={}", lib_path); // ライブラリ名を指定 println!("cargo:rustc-link-lib=AquesTalk2"); }
cargo buildすると、debugディレクトリの下にbindings.rsが生成されます。
ではsrc/main.rsで生成されたファイルをつかってみましょう。
main.rsでは指定した文字列をCスタイルの文字列に変換し、音声合成を行います。
wavデータを取得したらファイルに出力して処理は終了します。
use std::fs::File; use std::io::Write; use std::path::Path; use std::ffi::CString; use std::os::raw::c_char; include!(concat!(env!("OUT_DIR"), "/bindings.rs")); fn main() { // 生成する音声 let input_text = "こんにちわ。ひさしぶりのゆっくりボイスネタです"; let c_input_text = CString::new(input_text).expect("CString::new failed"); let c_input_ptr = c_input_text.as_ptr() as *const c_char; // 音声合成の速度 let speed = 100; // 音声データのサイズ let mut size = 0; // WAVデータ取得 let wav_ptr = unsafe { AquesTalk2_Synthe_Utf8(c_input_ptr, speed, &mut size, std::ptr::null_mut()) }; if !wav_ptr.is_null() { println!("output wav file."); let wav_slice = unsafe { std::slice::from_raw_parts(wav_ptr, size as usize) }; //wavファイルとして出力 let output_path = Path::new("output.wav"); let mut file = File::create(&output_path).expect("Failed to create WAV file"); file.write_all(wav_slice).expect("Failed to write WAV data to file"); file.flush().expect("Failed to flush WAV file"); unsafe { AquesTalk2_FreeWave(wav_ptr); } } else { eprintln!("ERR:{}", size); } }
cargo runでプログラムを実行すると、wavファイルが生成されています。
再生してみるとおなじみのあの声で音声が再生されます。
Summary
今回はbindgenを使ってAquesTalk2をRustから使ってみました。
自動でインターフェイスを生成してそのままRustから使えるので便利です。